package scorer;

import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JTextArea;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo;
import javax.swing.border.EtchedBorder;

import scorer.Tile.SUIT;
import org.python.core.Py;
import org.python.core.PySystemState;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class ScorerMain {

	static ArrayList<Tile> tiles;

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		tiles = new ArrayList<Tile>();

		try {
			for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
				if ("Nimbus".equals(info.getName())) {
					UIManager.setLookAndFeel(info.getClassName());
					break;
				}
			}
			ScorerFrame f = new ScorerFrame();
			f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
			f.setVisible(true);

		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	public static void generateTiles(ArrayList<Tile> tiles, int howMany)
			throws Exception {
		for (Object o : Character.getValidValues()) {
			for (int i = 0; i < howMany; i++) {
				tiles.add(new Character(o, SUIT.Character));
			}
		}
		for (Object o : Dots.getValidValues()) {
			for (int i = 0; i < howMany; i++) {
				tiles.add(new Dots(o, SUIT.Dots));
			}
		}
		for (Object o : Bamboo.getValidValues()) {
			for (int i = 0; i < howMany; i++) {
				tiles.add(new Bamboo(o, SUIT.Bamboo));
			}
		}
		for (Object o : Wind.getValidValues()) {
			for (int i = 0; i < howMany; i++) {
				tiles.add(new Wind(o, SUIT.Wind));
			}
		}
		for (Object o : Dragon.getValidValues()) {
			for (int i = 0; i < howMany; i++) {
				tiles.add(new Dragon(o, SUIT.Dragon));
			}
		}
		for (Object o : Flower.getValidValues()) {

			tiles.add(new Flower(o, SUIT.Flower));

		}
		for (Object o : Season.getValidValues()) {

			tiles.add(new Season(o, SUIT.Season));

		}
		;
	}

}

class ScorerFrame extends JFrame {

	private static final long serialVersionUID = 1L;

	public ScorerFrame() {
		setTitle("MahJong Scorer");
		setSize(850, 1000);
		Container content = getContentPane();

		tiles = new HashMap<Tile, BufferedImage>();
		try {
			ArrayList<Tile> temp = new ArrayList<Tile>();
			ScorerMain.generateTiles(temp, 1);
			for (Tile t : temp) {
				tiles.put(t, null);
			}
			createImages();
		} catch (Exception e) {
			e.printStackTrace();
		}

		tilePanel = new JPanel(new GridLayout(5, 1));
		tilePanel.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED));
		// addButtons(tilePanel);
		
		sets = new SetPanel[5];

		sets[0] = new SetPanel("pung");
		sets[1] = new SetPanel("pung");
		sets[2] = new SetPanel("pung");
		sets[3] = new SetPanel("pung");
		sets[4] = new SetPanel("eyes");
		
		for(SetPanel s: sets) {
			tilePanel.add(s);
		}

		JPanel main = new JPanel(new GridLayout(1,2));
		main.setBorder(BorderFactory.createLineBorder(Color.black, 3));
		
		main.add(tilePanel);

		JPanel side = new JPanel(new GridLayout(3,1));
		
		JPanel misc = new JPanel(new GridLayout(5, 2));
		misc.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED));

		seat = new JComboBox(new String[] { "East", "North", "West", "South" });
		prevailing = new JComboBox(new String[] { "East", "North", "West",
				"South" });

		kong = new JCheckBox("Replacement tile win");
		rob = new JCheckBox("Robbed the kong");
		last_turn = new JCheckBox("Last turn");
		self_draw = new JCheckBox("Self-draw");
		
		flowers = new JSpinner(new SpinnerNumberModel(0,0,8,1));

		misc.add(new JLabel("Seat Wind:"));
		misc.add(seat);
		misc.add(new JLabel("Prevailing Wind:"));
		misc.add(prevailing);
		misc.add(kong);
		misc.add(rob);
		misc.add(last_turn);
		misc.add(self_draw);
		misc.add(new JLabel("Number of Flowers:"));
		misc.add(flowers);


		JButton clear = new JButton("Clear hand");
		clear.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				score.setText("Score: ");
				kong.setSelected(false);
				rob.setSelected(false);
				last_turn.setSelected(false);
				self_draw.setSelected(false);
				fanNames.setText("");
				tilePanel.removeAll();
				sets[0] = new SetPanel("pung");
				sets[1] = new SetPanel("pung");
				sets[2] = new SetPanel("pung");
				sets[3] = new SetPanel("pung");
				sets[4] = new SetPanel("eyes");
				for(SetPanel s: sets) {
					tilePanel.add(s);
				}
				flowers.setValue(0);
				repaint();
			}
		});

		JButton scoreButton = new JButton("Score hand");
		scoreButton.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				try {
					String s = getHandAsString();
					boolean ok = s.matches(".* w .. .*");
					if (ok) {
						String hand_score = checkFan(s) + "";
						if (!hand_score.equals("0"))
							score.setText("Score: " + hand_score);
						else {
							score.setText("Not a mahjong!");
						}
					}else {
						fanNames.setText("Error: No winning tile selected!");
					}
				} catch (ScriptException e1) {
					e1.printStackTrace();
				}

			}
		});

		score = new JLabel("Score: ");
		score.setHorizontalAlignment(SwingConstants.CENTER);
		score.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED));

		fanNames = new JTextArea();
		JScrollPane editorScrollPane = new JScrollPane(fanNames);
		editorScrollPane
				.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
		editorScrollPane.setPreferredSize(new Dimension(250, 145));
		editorScrollPane.setMinimumSize(new Dimension(10, 10));

		JPanel buttonPanel = new JPanel(new GridLayout(1, 4));
		buttonPanel.setBorder(BorderFactory
				.createEtchedBorder(EtchedBorder.RAISED));
		buttonPanel.add(clear);
		buttonPanel.add(scoreButton);
		buttonPanel.add(score);
		
		side.add(misc);
		side.add(buttonPanel);
		side.add(editorScrollPane);

		main.add(side);
		
		content.add(main);
		
		try {
			engine = makeEngine();
		} catch (ScriptException e1) {
			e1.printStackTrace();
		}

	}

	JPanel tilePanel;
	SetPanel[] sets;
	JComboBox seat;
	JComboBox prevailing;
	JCheckBox kong, rob, last_turn, self_draw;
	JSpinner flowers;
	JLabel score;
	JTextArea fanNames;
	HashMap<Tile, BufferedImage> tiles;

	class SetPanel extends JPanel {

		private static final long serialVersionUID = 1L;
		public ArrayList<TilePanel> tilePanels;
		public JCheckBox open;
		public JComboBox type;
		private JPanel choices;

		public SetPanel(String start) {
			super(new GridLayout(1, 5));
			tilePanels = new ArrayList<TilePanel>();
			open = new JCheckBox("Open");
			choices = new JPanel(new GridLayout(2, 1));
			type = new JComboBox(
					new String[] { "chow", "pung", "kong", "eyes" });
			type.addActionListener(new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					open.setEnabled(true);
					if (type.getSelectedItem().equals("kong")
							&& tilePanels.size() < 4) {
						addCombo();
						if (tilePanels.size() < 4)
							addCombo();
					} else if (tilePanels.size() > 3) {
						removeCombo();
						if (type.getSelectedItem().equals("eyes")) {
							open.setSelected(false);
							open.setEnabled(false);
							removeCombo();
						}
					}

					buildPanel();
				}
			});

			type.setSelectedItem(start);

			addCombo();
			addCombo();
			if(start.equals("eyes")) {
				open.setSelected(false);
				open.setEnabled(false);
			}else {
				addCombo();
			}

			choices.add(type);
			choices.add(open);

			buildPanel();
		}

		private void removeCombo() {
			tilePanels.remove(tilePanels.size() - 1);
		}

		private void addCombo() {
			tilePanels.add(new TilePanel());
		}

		private void buildPanel() {
			this.removeAll();
			this.validate();
			this.add(choices);
			for (TilePanel t : tilePanels) {
				this.add(t);
			}
			this.validate();
			this.repaint();
		}

		public String getSet() {
			String set = "";
			String win = "";
			if (type.getSelectedItem().equals("kong") && open.isSelected())
				set += " c ";
			else if (open.isSelected())
				set += " m ";
			else
				set += " h ";
			for (TilePanel t : tilePanels) {
				if (!t.winner.isSelected())
					set += t.getTile();
				else
					win = " w "+t.getTile()+" ";
			}
			System.out.println(set+" "+win);
			return set + " " + win;
		}

		class TilePanel extends JPanel {
			private static final long serialVersionUID = 1L;
			public MyComboBox[] choosers;
			JLabel picture;
			JPanel combos;
			JCheckBox winner;
			Tile tile;

			public TilePanel() {
				super(new GridLayout(3, 1));
				choosers = new MyComboBox[2];
				choosers[0] = new MyComboBox(Tile.SUIT.values(), null);
				choosers[1] = new MyComboBox(choosers[0]);
				choosers[0].link = choosers[1];
				choosers[0].addActionListener(new ActionListener() {
					@Override
					public void actionPerformed(ActionEvent e) {
						MyComboBox srce = (MyComboBox) e.getSource();
						ArrayList<Object> validValues = null;
						switch ((Tile.SUIT) srce.getSelectedItem()) {
						case Wind:
							validValues = Wind.getValidValues();
							break;
						case Dragon:
							validValues = Dragon.getValidValues();
							break;
						default:
							validValues = Character.getValidValues();
							break;
						}
						srce.link.removeAllItems();
						for (Object anObject : validValues) {
							srce.link.addItem(anObject);
						}
					}
				});
				picture = new JLabel();
				choosers[1].addActionListener(new ActionListener() {
					@Override
					public void actionPerformed(ActionEvent e) {
						SUIT s = (SUIT) ((MyComboBox) e.getSource()).link
								.getSelectedItem();
						Object value = ((MyComboBox) e.getSource())
								.getSelectedItem();
						for (Tile t : tiles.keySet()) {
							if (t.suit == s && t.value.equals(value)) {
								picture.setIcon(new ImageIcon(tiles.get(t)));
								tile = t;
								break;
							}
						}
					}
				});

				choosers[0].setSelectedIndex(0);
				
				winner = new JCheckBox("Winning tile");

				combos = new JPanel(new GridLayout(2, 1));
				combos.add(choosers[0]);
				combos.add(choosers[1]);
				this.add(picture);
				this.add(combos);
				this.add(winner);
			}

			public String getTile() {
				return tile.toString();
			}

			class MyComboBox extends JComboBox {
				private static final long serialVersionUID = 1L;
				protected MyComboBox link;

				public MyComboBox(MyComboBox l) {
					super();
					link = l;
				}

				public MyComboBox(Object[] objs, MyComboBox l) {
					super(objs);
					link = l;
				}
			}
		}

	}

	public void createImages() throws Exception {
		for (Tile t : tiles.keySet()) {
			XpmParser parser = new XpmParser(getClass().getResourceAsStream(
					"tiles-numbered/" + t.getValue().toString().substring(0, 1)
							+ t.getClass().getSimpleName().substring(0, 1)
							+ ".xpm"));
			if (!parser.parse())
				throw new Exception("Error: Failed to parse file.");
			// System.out
			// .println(parser.getWidth() + " " + parser.getHeight());
			// for(Color c:parser.getColorTable()) {
			// System.out.println(c);
			// }
			// int count = 0;
			// for (byte b : parser.getPixmap()) {
			// System.out.print(b);
			// count++;
			// if (count % parser.getWidth() == 0)
			// System.out.println("");
			// }
			tiles.put(t, drawXPM(parser));
		}
	}

	public BufferedImage drawXPM(XpmParser parser) {
		// create color map
		Color[] colorTable = parser.getColorTable();

		BufferedImage img = new BufferedImage(parser.getWidth(),
				parser.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
		byte[] pix = parser.getPixmap();
		int w = parser.getWidth(), h = parser.getHeight();
		for (int i = 0; i < h; i++) {
			for (int j = 0; j < w; j++) {
				img.setRGB(j, i, colorTable[pix[j + w * i]].getRGB());
			}

		}
		BufferedImage scaledImage = new BufferedImage(w * 3 / 2, h * 3 / 2,
				BufferedImage.TYPE_INT_ARGB);
		Graphics2D graphics2D = scaledImage.createGraphics();
		AffineTransform xform = AffineTransform.getScaleInstance(1.5, 1.5);
		graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
				RenderingHints.VALUE_INTERPOLATION_BICUBIC);
		graphics2D.drawImage(img, xform, null);
		graphics2D.dispose();
		return scaledImage;

	}

	public int checkFan(String s) throws ScriptException {
		String usedfans = "";
		int score = 0;
		engine.eval("m = find_mahjong('" + s + "')");

		// extract the is_mahjong
		System.out.println(engine.get("m"));
		engine.eval("is_mahjong = m['is_mahjong']");
		boolean isMahjong = getAsBoolean("is_mahjong");

		if (isMahjong) {
			// extract the score
			engine.eval("score = m['score']");

			score = getAsInt("score");
			for (Fan fan : extractFan()) {
				usedfans += fan.toString() + "\n";
			}
		}
		fanNames.setText(usedfans);
		return score;
	}

	private List<Fan> extractFan() throws ScriptException {
		// extract the nb of fans
		engine.eval("nbFans = len(m['fans'])");
		int nbFans = getAsInt("nbFans");

		// extract fans
		ArrayList<Fan> tempFans = new ArrayList<Fan>();
		for (int i = 0; i < nbFans; i++) {
			// extract points
			engine.eval("points = m['fans'][" + i + "]['points']");
			int points = getAsInt("points");
			// extract tiles
			engine.eval("tiles = m['fans'][" + i + "]['tiles']");
			// extract name
			engine.eval("name = m['fans'][" + i + "]['name']");
			String name = getAsString("name");
			// add to fan list
			tempFans.add(new Fan(points, name, getAsString("tiles")));
		}
		;
		return tempFans;
	}

	private int getAsInt(String pyVar) {
		return (Integer) engine.get(pyVar);
	}

	private String getAsString(String pyVar) {
		return (String) engine.get(pyVar);
	}

	private boolean getAsBoolean(String pyVar) {
		return (Boolean) engine.get(pyVar);
	}

	private String getHandAsString() {
		String h = "";

		for(SetPanel s:sets) {
			h += s.getSet();
		}
		
		if((Integer)flowers.getValue()!=0) {
			h += " f ";
			int numFlowers = (Integer)flowers.getValue();
			for(int i=1; i <= numFlowers; i++) {
				h += "F"+i;
			}
		}
		
		h += " sw W"
				+ seat.getSelectedItem().toString().substring(0, 1)
						.toLowerCase()
				+ " rw W"
				+ prevailing.getSelectedItem().toString().substring(0, 1)
						.toLowerCase() + " ";
		if (kong.isSelected()) {
			h += "kong_replacement ";
		}
		if (rob.isSelected()) {
			h += "robbing ";
		}
		if (last_turn.isSelected()) {
			h += "last_turn ";
		}
		if (self_draw.isSelected()) {
			h += "self_draw ";
		}
		System.out.println(h);

		return h;
	}

	ScriptEngine engine;

	private ScriptEngine makeEngine() throws ScriptException {
		System.out.println("PythonEngine.makeEngine");
		PySystemState engineSys = new PySystemState();
		System.out.println("Python path:" + engineSys.path.toString());
		Py.setSystemState(engineSys);
		System.out.println("SystemState added");
		ScriptEngine engine = new ScriptEngineManager()
				.getEngineByName("python");
		System.out.println("Got engine");
		engine.eval("from pymcr.majiang import find_mahjong");
		System.out.println("import of mahjong done");
		return engine;
	}

}
